﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DriveBeamEditor.Controls.BeamNG.Controls;
using DriveBeamEditor.Controls.BeamNG.Forms;
using DriveBeamEditor.Controls.BeamNG.SectionControls;
using DriveBeamEditor.Controls.BeamNG.VehiclePartConfig;
using DriveBeamEditor.Controls.General;
using DriveBeamEditor.Enums;
using DriveBeamEditor.Forms.Docking;
using DriveBeamEditor.Properties;
using DriveBeamEditor.Tools;
using JBeamLibrary;
using JBeamLibrary.VehicleParts;
using JBeamLibrary.VehicleParts.Groups;
using NativeLibrary;
using WeifenLuo.WinFormsUI.Docking;

namespace DriveBeamEditor.Controls.BeamNG
{
    public partial class BeamNGMainForm : Form, IMessageFilter
    {
        #region Fields

        private MdiClient _mdiClient;
        private Vehicle _selectedVehicle = null;

        VehicleSectionContentForm _generalForm;
        VehicleSectionContentForm _propsForm;
        VehicleSectionContentForm _nodesForm;
        VehicleSectionContentForm _beamsForm;
        VehicleSectionContentForm _flexbodiesForm;
        VehicleSectionContentForm _camerasForm;
        VehicleSectionContentForm _engineForm;
        VehicleSectionContentForm _differentialsForm;
        VehicleSectionContentForm _hubWheelsForm;
        VehicleSectionContentForm _trianglesForm;
        VehicleSectionContentForm _hydrosForm;
        VehicleSectionContentForm _thrustersForm;
        VehicleSectionContentForm _glowMapForm;
        VehicleSectionContentForm _tiesForm;
        VehicleSectionContentForm _editorForm;
        VehicleContentForm _hierarchyForm;
        WebBrowserContentForm _internetForm;
        VehicleContentForm _configuratorForm;
        VehicleContentForm _gameForm;

        /* Lets hard-code these so that code must be edited to change them ;) */
        private static readonly string ForumUrl = @"http://www.beamng.com/threads/6223";
        private static readonly string WikiPage = @"http://wiki.beamng.com/Real-Time_Vehicle_Editor_Guide";

        public event EventHandler<VehiclePart> SelectedVehiclePartChanged;
        public event EventHandler<Vehicle> SelectedVehicleChanged;
        public event EventHandler<string> BrowserUrlChanged;

        #endregion

        #region Properties

        protected MdiClient MdiClient
        {
            get { return _mdiClient; }
            private set { _mdiClient = value; }
        }

        protected VehiclePartConfigControl GeneralControl
        {
            get { return _generalForm.Control as VehiclePartConfigControl; }
        }

        protected PropsControl PropsControl
        {
            get { return _propsForm.Control as PropsControl; }
        }

        protected NodesControl NodesControl
        {
            get { return _nodesForm.Control as NodesControl; }
        }

        protected BeamsControl BeamsControl
        {
            get { return _beamsForm.Control as BeamsControl; }
        }

        protected FlexbodiesControl FlexbodiesControl
        {
            get { return _flexbodiesForm.Control as FlexbodiesControl; }
        }

        protected CamerasControl CamerasControl
        {
            get { return _camerasForm.Control as CamerasControl; }
        }

        protected EngineControl EngineControl
        {
            get { return _engineForm.Control as EngineControl; }
        }

        protected DifferentialsControl DifferentialsControl
        {
            get { return _differentialsForm.Control as DifferentialsControl; }
        }

        protected HubWheelsControl HubWheelsControl
        {
            get { return _hubWheelsForm.Control as HubWheelsControl; }
        }

        protected TrianglesControl TrianglesControl
        {
            get { return _trianglesForm.Control as TrianglesControl; }
        }

        protected HydrosControl HydrosControl
        {
            get { return _hydrosForm.Control as HydrosControl; }
        }

        protected ThrustersControl ThrustersControl
        {
            get { return _thrustersForm.Control as ThrustersControl; }
        }

        protected GlowMapControl GlowMapControl
        {
            get { return _glowMapForm.Control as GlowMapControl; }
        }

        protected TiesControl TiesControl
        {
            get { return _tiesForm.Control as TiesControl; }
        }

        protected NodeBeam3DEditorControl Editor3D
        {
            get { return _editorForm.Control as NodeBeam3DEditorControl; }
        }

        protected VehicleHierarchyControl StructureControl
        {
            get { return _hierarchyForm.Control as VehicleHierarchyControl; }
        }

        protected WebBrowser Browser
        {
            get { return _internetForm.Control; }
        }

        protected ConfiguratorControl ConfiguratorControl
        {
            get { return _configuratorForm.Control as ConfiguratorControl; }
        }

        protected Controls.GameControl GameControl
        {
            get { return _gameForm.Control as Controls.GameControl; }
        }

        #endregion

        #region CTORs

        public BeamNGMainForm()
        {
            InitializeComponent();
            InitializeStyles();
            InitializeMdiClient();
            InitializeForms();

            SetupLayout((ApplicationLayout)UIHelper.ValueFromString(Config.Map["applicationLayout"]));
            ConfigureControls();
        }

        #endregion

        #region Events

        #region Overrides

        protected override void OnLoad(EventArgs e)
        {
            /* Version label */
            var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
            var title = string.Empty;
            if (version.ProductMajorPart == 0 && version.ProductMinorPart == 0 && version.ProductBuildPart == 0)
                title = "Alpha";
            else if (version.ProductMajorPart == 0 && version.ProductMinorPart == 0)
                title = "Beta";
            else
                title = "Release";

            labelVersion.Text = string.Format(
                "{0} {1}.{2}.{3}.{4}",
                title,
                version.ProductMajorPart,
                version.ProductMinorPart,
                version.ProductBuildPart,
                version.ProductPrivatePart);

            base.OnLoad(e);
        }

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);

            QuickTipBox.Show(this, "Application ready");
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            if (GameControl.IsGameRunning)
                GameControl.TerminateGame();
            e.Cancel = false;
            base.OnClosing(e);
        }

        #endregion

        #region Dock Panel

        private void dockPanel_ActiveContentChanged(object sender, EventArgs e)
        {
            if (dockPanel.ActiveContent is VehicleContentForm)
            {
                var control = ((VehicleContentForm)dockPanel.ActiveContent).Control;
            }
            else if (dockPanel.ActiveContent is VehicleSectionContentForm)
            {
                var control = ((VehicleSectionContentForm)dockPanel.ActiveContent).Control;
                control.RefreshData();
            }
        }

        #endregion

        #region Buttons

        private void xbMaxSize_Click(object sender, EventArgs e)
        {
            if (xbMaxSize.Text.Equals("+"))
                SetMaxSize();
            else
                SetNormalSize();
        }

        private void xbHelp_Click(object sender, EventArgs e)
        {
            if (xbHelp.Text.Equals("?"))
                ShowHelp();
            else
                ShowForumPage();
        }

        private void donateButton_Click(object sender, EventArgs e)
        {
            Donate();
        }

        #endregion

        #region Context menu & popups

        private void xbVehicle_MouseClick(object sender, MouseEventArgs e)
        {
            ppdParts.Hide();
            pddPreferences.Hide();

            if (pddVehicle.Visible)
                pddVehicle.Close(ToolStripDropDownCloseReason.CloseCalled);
            else
                pddVehicle.Show(xbVehicle);
        }

        private void xbVehicle_MouseEnter(object sender, EventArgs e)
        {
            ppdParts.Hide();
            pddPreferences.Hide();

            if (!pddVehicle.Visible)
                pddVehicle.Show(xbVehicle);
        }

        private void xbParts_MouseClick(object sender, MouseEventArgs e)
        {
            pddVehicle.Hide();
            pddPreferences.Hide();

            if (ppdParts.Visible)
                ppdParts.Close(ToolStripDropDownCloseReason.CloseCalled);
            else
                ppdParts.Show(xbParts);
        }

        private void xbParts_MouseEnter(object sender, EventArgs e)
        {
            pddVehicle.Hide();
            pddPreferences.Hide();

            if (!ppdParts.Visible)
                ppdParts.Show(xbParts);
        }

        private void xbPreferences_MouseClick(object sender, MouseEventArgs e)
        {
            pddVehicle.Hide();
            ppdParts.Hide();

            if (pddPreferences.Visible)
                pddPreferences.Close(ToolStripDropDownCloseReason.CloseCalled);
            else
                pddPreferences.Show(xbPreferences);
        }

        private void xbPreferences_MouseEnter(object sender, EventArgs e)
        {
            pddVehicle.Hide();
            ppdParts.Hide();

            if (!pddPreferences.Visible)
                pddPreferences.Show(xbPreferences);
        }

        #endregion

        #region Vehicle popup

        private void pcVehicle_VehicleOpened(object sender, Vehicle e)
        {
            pcVehicle.Close();

            var item = vehiclesMenu.Items.OfType<ImageMenu>().FirstOrDefault(im => ((Vehicle)im.Tag).Name.Equals(e.Name));
            if (item != null) vehiclesMenu.RemoveItem(item);

            if (e == null) return;

            item =
                vehiclesMenu.Items.OfType<ImageMenu>().FirstOrDefault(im => ((Vehicle)im.Tag).Name.Equals(e.Name)) ??
                vehiclesMenu.AddItem(e.Label, e, Resources.Ferrari_64);
            vehiclesMenu.SelectItem(item);

            _hierarchyForm.Show();

            QuickTipBox.Show(this, "Vehicle opened");
        }

        private void pcVehicle_VehicleSaved(object sender, JBeamLibrary.Vehicle e)
        {
            /* Update game control */
            if (GameControl.IsGameRunning)
                GameControl.UpdateGame();

            QuickTipBox.Show(this, "Vehicle saved");
        }

        #endregion

        #region Preferences popup

        private void pddPreferences_Closed(object sender, ToolStripDropDownClosedEventArgs e)
        {
            pcPreferences.SaveConfig();
        }

        #endregion

        #region Vehicle menu bar

        private void vehiclesMenu_ItemSelected(object sender, ImageMenu e)
        {
            SelectVehicle(e.Tag as Vehicle);
        }

        private void vehiclesMenu_ItemRemoving(object sender, object e)
        {
            var vehicle = e as Vehicle;
            if (vehicle == null) return;

            if (MessageBox.Show(this, @"Would you like to save the vehicle changes ?", @"Save changes ?",
                MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                vehicle.Save(true);
            }
        }

        private void vehiclesMenu_ItemRemoved(object sender, EventArgs e)
        {
            SelectVehicle(null);

            _hierarchyForm.Show();
            QuickTipBox.Show(this, "Vehicle closed");
        }

        #endregion

        #region Hierarchy control

        private void vehicleHierarchyControl_VehiclePartValidated(object sender, VehiclePart e)
        {
            SelectVehiclePart(e);
        }

        #endregion

        #region Game control

        private void gameControl_RequestSave(object sender, EventArgs e)
        {
            if (_selectedVehicle == null) return;

            /* Save current vehicle */
            _selectedVehicle.Save();

            /* Update the game */
            GameControl.UpdateGame();

            QuickTipBox.Show(this, "Vehicle saved");
        }

        #endregion

        #endregion

        #region Methods

        #region User Interface

        private void InitializeStyles()
        {
            SetStyle(
                ControlStyles.AllPaintingInWmPaint |
                ControlStyles.DoubleBuffer |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.ResizeRedraw |
                ControlStyles.SupportsTransparentBackColor |
                ControlStyles.UserPaint,
                true
            );
            Application.AddMessageFilter(this);
        }

        private void InitializeMdiClient()
        {
            _mdiClient = Controls.OfType<MdiClient>().FirstOrDefault();
        }

        private void InitializeForms()
        {
            _generalForm = new VehicleSectionContentForm(new VehiclePartConfigControl(), this);
            _propsForm = new VehicleSectionContentForm(new PropsControl(), this);
            _nodesForm = new VehicleSectionContentForm(new NodesControl(), this);
            _beamsForm = new VehicleSectionContentForm(new BeamsControl(), this);
            _flexbodiesForm = new VehicleSectionContentForm(new FlexbodiesControl(), this);
            _camerasForm = new VehicleSectionContentForm(new CamerasControl(), this);
            _engineForm = new VehicleSectionContentForm(new EngineControl(), this);
            _differentialsForm = new VehicleSectionContentForm(new DifferentialsControl(), this);
            _hubWheelsForm = new VehicleSectionContentForm(new HubWheelsControl(), this);
            _trianglesForm = new VehicleSectionContentForm(new TrianglesControl(), this);
            _hydrosForm = new VehicleSectionContentForm(new HydrosControl(), this);
            _thrustersForm = new VehicleSectionContentForm(new ThrustersControl(), this);
            _glowMapForm = new VehicleSectionContentForm(new GlowMapControl(), this);
            _tiesForm = new VehicleSectionContentForm(new TiesControl(), this);
            _editorForm = new VehicleSectionContentForm(new NodeBeam3DEditorControl(), this);
            _hierarchyForm = new VehicleContentForm(new VehicleHierarchyControl(), this);
            _internetForm = new WebBrowserContentForm(ForumUrl, this);
            _configuratorForm = new VehicleContentForm(new ConfiguratorControl(), this);
            _gameForm = new VehicleContentForm(new Controls.GameControl(), this);
        }

        public void SetupLayout(ApplicationLayout layout)
        {
            switch (layout)
            {
                case ApplicationLayout.SplitScreen:
                    SetSplitScreen();
                    break;

                case ApplicationLayout.DualScreen:
                    SetDualScreen();
                    break;

                case ApplicationLayout.Edition3D:
                    SetEdition3D();
                    break;
            }
            
        }

        private void SetSplitScreen()
        {
            dockPanel.SuspendLayout(true);

            _tiesForm.Show(dockPanel, DockState.Document);
            _glowMapForm.Show(_tiesForm.Pane, _tiesForm);
            _thrustersForm.Show(_glowMapForm.Pane, _glowMapForm);
            _hydrosForm.Show(_thrustersForm.Pane, _thrustersForm);
            _trianglesForm.Show(_hydrosForm.Pane, _hydrosForm);
            _hubWheelsForm.Show(_trianglesForm.Pane, _trianglesForm);
            _differentialsForm.Show(_hubWheelsForm.Pane, _hubWheelsForm);
            _engineForm.Show(_differentialsForm.Pane, _differentialsForm);
            _camerasForm.Show(_engineForm.Pane, _engineForm);
            _flexbodiesForm.Show(_camerasForm.Pane, _camerasForm);
            _beamsForm.Show(_flexbodiesForm.Pane, _flexbodiesForm);
            _nodesForm.Show(_beamsForm.Pane, _beamsForm);
            _propsForm.Show(_nodesForm.Pane, _nodesForm);
            _generalForm.Show(_propsForm.Pane, _propsForm);
            _hierarchyForm.Show(_generalForm.PanelPane, _generalForm);
            _gameForm.Show(dockPanel, DockState.DockRightAutoHide);
            _configuratorForm.Show(dockPanel, DockState.DockLeftAutoHide);
            _editorForm.Show(_generalForm.Pane, DockAlignment.Right, 0.5);
            _internetForm.Show(_editorForm.PanelPane, _editorForm);

            dockPanel.ResumeLayout(true, true);
        }

        private void SetDualScreen()
        {
            /* Set to split screen if user doesn't have a second screen */
            if (Screen.AllScreens.Length < 2)
            {
                SetSplitScreen();
                return;
            }

            /* Get second screen bounds */
            var secondBounds = Screen.AllScreens[1].WorkingArea;

            dockPanel.SuspendLayout(true);

            _tiesForm.Show(dockPanel, DockState.Document);
            _glowMapForm.Show(_tiesForm.Pane, _tiesForm);
            _thrustersForm.Show(_glowMapForm.Pane, _glowMapForm);
            _hydrosForm.Show(_thrustersForm.Pane, _thrustersForm);
            _trianglesForm.Show(_hydrosForm.Pane, _hydrosForm);
            _hubWheelsForm.Show(_trianglesForm.Pane, _trianglesForm);
            _differentialsForm.Show(_hubWheelsForm.Pane, _hubWheelsForm);
            _engineForm.Show(_differentialsForm.Pane, _differentialsForm);
            _camerasForm.Show(_engineForm.Pane, _engineForm);
            _flexbodiesForm.Show(_camerasForm.Pane, _camerasForm);
            _beamsForm.Show(_flexbodiesForm.Pane, _flexbodiesForm);
            _nodesForm.Show(_beamsForm.Pane, _beamsForm);
            _propsForm.Show(_nodesForm.Pane, _nodesForm);
            _generalForm.Show(_propsForm.Pane, _propsForm);
            _hierarchyForm.Show(_generalForm.PanelPane, _generalForm);
            _gameForm.Show(dockPanel, DockState.DockRightAutoHide);
            _configuratorForm.Show(dockPanel, DockState.DockLeftAutoHide);

            _editorForm.Show(dockPanel, secondBounds);
            _internetForm.Show(_editorForm.FloatPane, _editorForm);

            dockPanel.ResumeLayout(true, true);
        }

        private void SetEdition3D()
        {
            dockPanel.SuspendLayout(true);

            _tiesForm.Show(dockPanel, DockState.Document);
            _glowMapForm.Show(_tiesForm.Pane, _tiesForm);
            _thrustersForm.Show(_glowMapForm.Pane, _glowMapForm);
            _hydrosForm.Show(_thrustersForm.Pane, _thrustersForm);
            _trianglesForm.Show(_hydrosForm.Pane, _hydrosForm);
            _hubWheelsForm.Show(_trianglesForm.Pane, _trianglesForm);
            _differentialsForm.Show(_hubWheelsForm.Pane, _hubWheelsForm);
            _engineForm.Show(_differentialsForm.Pane, _differentialsForm);
            _camerasForm.Show(_engineForm.Pane, _engineForm);
            _flexbodiesForm.Show(_camerasForm.Pane, _camerasForm);
            _beamsForm.Show(_flexbodiesForm.Pane, _flexbodiesForm);
            _nodesForm.Show(_beamsForm.Pane, _beamsForm);
            _propsForm.Show(_nodesForm.Pane, _nodesForm);
            _generalForm.Show(_propsForm.Pane, _propsForm);
            _hierarchyForm.Show(_generalForm.PanelPane, _generalForm);
            _gameForm.Show(dockPanel, DockState.DockRightAutoHide);
            _configuratorForm.Show(dockPanel, DockState.DockLeftAutoHide);

            _editorForm.Show(_generalForm.Pane, _generalForm);
            _internetForm.Show(_editorForm.PanelPane, _editorForm);

            dockPanel.ResumeLayout(true, true);
        }

        private void ConfigureControls()
        {
            dockPanel.DefaultFloatWindowSize = Screen.PrimaryScreen.WorkingArea.Size;
            StructureControl.VehiclePartValidated += vehicleHierarchyControl_VehiclePartValidated;
            GameControl.RequestSave += gameControl_RequestSave;
        }

        private void SetMaxSize()
        {
            xbMaxSize.Text = @"-";

            SuspendLayout();

            /* Header */
            panelHeader.BackgroundImage = null;
            panelHeader.Height = 70;

            /* Move menu buttons */
            xbVehicle.Location = new Point(38, 16);
            xbParts.Location = new Point(212, 16);
            xbPreferences.Location = new Point(386, 16);

            /* Move vehicles menu */
            vehiclesMenu.Location = new Point(560, 0);
            vehiclesMenu.Width = ClientSize.Width - xbPreferences.Right - (ClientSize.Width - labelDonate.Left);

            ResumeLayout(true);
        }

        private void SetNormalSize()
        {
            xbMaxSize.Text = @"+";

            SuspendLayout();

            /* Reset menu buttons */
            xbVehicle.Location = new Point(169, 103);
            xbParts.Location = new Point(343, 103);
            xbPreferences.Location = new Point(517, 103);

            /* Reset vehicles menu */
            vehiclesMenu.Location = new Point(689, 84);
            vehiclesMenu.Width = ClientSize.Width - xbPreferences.Right - (ClientSize.Width - xbHelp.Left);

            /* Reset header */
            panelHeader.Height = 160;
            panelHeader.BackgroundImage = Resources.TitleBar2;

            ResumeLayout(true);
        }

        private void ShowForumPage()
        {
            xbHelp.Text = @"?";
            _internetForm.Show();

            if (BrowserUrlChanged != null)
                BrowserUrlChanged(this, ForumUrl);
        }

        private void ShowHelp()
        {
            xbHelp.Text = @"@";
            _internetForm.Show();

            if (BrowserUrlChanged != null)
                BrowserUrlChanged(this, WikiPage);
        }

        private void Donate()
        {
            _internetForm.Show();

            if (BrowserUrlChanged != null)
                BrowserUrlChanged(this, DonateButton.DonateUrl);
        }

        #endregion

        #region Vehicle

        private void SelectVehicle(Vehicle vehicle)
        {
            /* Set selected vehicle */
            _selectedVehicle = vehicle;

            /* Bind to controls */
            //vehicleHierarchyControl.Vehicle = vehicle;
            //configuratorControl.Vehicle = vehicle;
            //gameControl.Vehicle = vehicle;
            pcVehicle.CurrentVehicle = vehicle;

            /* Notify */
            if (SelectedVehicleChanged != null)
                SelectedVehicleChanged(this, vehicle);

            /* Reset the current part on vehicle part controls */
            SelectVehiclePart(null);

            /* Auto select next loaded vehicle */
            if (vehicle == null && vehiclesMenu.ItemCount > 0)
                vehiclesMenu.SelectItemAt(0);
        }

        private void SelectVehiclePart(VehiclePart part)
        {
            if (SelectedVehiclePartChanged != null)
                SelectedVehiclePartChanged(this, part);

            if (part != null)
            {
                _generalForm.Show();
                _editorForm.Show();
                QuickTipBox.Show(this, "Vehicle part opened");
            }
        }

        #endregion

        #endregion

        #region IMessageFilter

        public bool PreFilterMessage(ref Message m)
        {
            var handled = false;
            switch (m.Msg)
            {
                /* KEY UP */
                case (int)WindowsMessages.WM_KEYUP:
                    var key = (Keys)(int)m.WParam & Keys.KeyCode;
                    switch (key)
                    {
                        case Keys.S:
                            if (ModifierKeys.HasFlag(Keys.Control) && _selectedVehicle != null)
                            {
                                handled = true;
                                _selectedVehicle.Save();
                                QuickTipBox.Show(this, "Vehicle saved");
                            }
                            break;
                        case Keys.F4:
                            if (ModifierKeys.HasFlag(Keys.Control) && _selectedVehicle != null)
                            {
                                handled = true;
                                vehiclesMenu.RemoveItemByTag(_selectedVehicle);
                                QuickTipBox.Show(this, "Vehicle closed");
                            }
                            break;
                    }
                    break;
            }
            return handled;
        }

        #endregion
    }
}
